﻿using System;
using System.Net.Sockets;
using System.Threading;

namespace ALProtocol.Tcp
{
    /// <summary>
    /// 表示TCP数据读取。
    /// </summary>
    public abstract class TcpReading
    {
        /// <summary>
        /// 表示接收参数。
        /// </summary>
        private sealed class RecvArgs : SocketAsyncEventArgs
        {
            /// <summary>
            /// 所有者。
            /// </summary>
            private TcpReading m_owner;
            /// <summary>
            /// 实例化一个新的接收参数。
            /// </summary>
            /// <param name="owner"></param>
            public RecvArgs(TcpReading owner)
            {
                m_owner = owner;
            }
            /// <summary>
            /// 当接收完成后调用此方法。
            /// </summary>
            /// <param name="e"></param>
            protected override void OnCompleted(SocketAsyncEventArgs e)
            {
                m_owner.OnIOCompleted(e);
            }
        }

        /// <summary>
        /// 异步事件。
        /// </summary>
        private RecvArgs m_args;
        /// <summary>
        /// 数据长度。
        /// </summary>
        private int m_length;
        /// <summary>
        /// 数据缓存。
        /// </summary>
        private byte[] m_buffer;

        /// <summary>
        /// TCP会话。
        /// </summary>
        private TcpSession m_session;

        /// <summary>
        /// 获取状态，如果正在进行数据读取，则返回True，否则返回False。
        /// </summary>
        public bool Status
        {
            get { return m_session != null; }
        }

        /// <summary>
        /// 实例化一个新的TCP数据读取。
        /// </summary>
        /// <param name="buffer"></param>
        public TcpReading(int buffer)
        {
            m_buffer = new byte[buffer];
        }
        /// <summary>
        /// 实例化一个新的TCP数据读取。
        /// </summary>
        /// <param name="buffer"></param>
        public TcpReading(byte[] buffer)
        {
            m_buffer = buffer;
        }

        /// <summary>
        /// 执行IO处理。
        /// </summary>
        private void ExecIOProcess(TcpSession session)
        {
            if (session == null)
                return;
            Socket socket = session.Socket;
            if (socket == null)
                return;
            try
            {
                m_args.SetBuffer(m_buffer, m_length, m_buffer.Length - m_length);
                if (!socket.ReceiveAsync(m_args))
                {
                    this.OnIOCompleted(m_args);
                }
            }
            catch
            {
                session.Dispose();
            }
        }

        /// <summary>
        /// 执行IO处理。
        /// </summary>
        /// <param name="e"></param>
        private void OnIOCompleted(SocketAsyncEventArgs e)
        {
            TcpSession session = m_session;
            if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
            {
                byte[] array = m_buffer;
                int total = m_length + e.BytesTransferred;
                //执行数据处理。
                int count = this.ProcData(m_buffer, total);
                if (count > 0 && count != total)
                {
                    Array.Copy(array, total - count, array, 0, count);
                }
                m_length = count;
                this.ExecIOProcess(session);
            }
            else
            {
                if (session != null)
                    session.Dispose();
            }
        }

        /// <summary>
        /// 启动数据读取。
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        internal bool Start(TcpSession session)
        {
            if (Interlocked.CompareExchange<TcpSession>(ref m_session, session, null) == null)
            {
                m_length = 0;
                m_args = new RecvArgs(this);
                this.ExecIOProcess(m_session);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 关闭数据读取。
        /// </summary>
        internal void Close()
        {
            if (Interlocked.Exchange<TcpSession>(ref m_session, null) != null)
            {
                m_args.Dispose();
            }
        }

        /// <summary>
        /// 处理数据，返回余下的数据。
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        protected abstract int ProcData(byte[] buffer, int length);
    }
}
